package com.boot2.core.utils;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 
 * @ClassName: BeanUtils
 * @description: 扩展 spring下的BeanUtils包
 * @author zhangweilin
 * @date 2017年12月29日 下午3:59:40
 *
 */
public abstract class BeanUtils extends org.springframework.beans.BeanUtils {
	private static final Log logger = LogFactory.getLog(BeanUtils.class);

	/**
	 * 忽略空值的属性拷贝
	 * 
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	public static void copyProperties(Object source, Object target) throws BeansException {
		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");
		Class<?> actualEditable = target.getClass();
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		for (PropertyDescriptor targetPd : targetPds) {
			if (targetPd.getWriteMethod() != null) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null && sourcePd.getReadMethod() != null) {
					try {
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);
						// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
						if (value != null) {
							Method writeMethod = targetPd.getWriteMethod();
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							try {
								writeMethod.invoke(target, value);
							} catch (IllegalArgumentException e) {
								logger.warn("方法【" + writeMethod + "】调用失败，将忽略...", e);
							}
						}
					} catch (Throwable ex) {
						throw new FatalBeanException("Could not copy properties from source to target", ex);
					}
				}
			}
		}
	}

	/**
	 * 用apache下的工具，拷贝带日期的对象，并能自动进行Date和String之间的转换
	 * 
	 * @param source
	 * @param target
	 * @param pattern
	 */
	public static void copyPropertiesWithDataPattern(Object source, Object target, String... pattern) {
		// 处理时间格式
		DateConverter dateConverter = new DateConverter();
		// 设置日期格式
		dateConverter.setPatterns(pattern);
		// 注册格式
		ConvertUtils.register(dateConverter, Date.class);
		try {
			org.apache.commons.beanutils.BeanUtils.copyProperties(target, source);
		} catch (IllegalAccessException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static <T> List<T> copyList(Iterable<? extends Object> sourceList, Class<? extends T> clazz, String... pattern) {
		List<T> targetList = new ArrayList<>();
		Assert.notNull(sourceList, "Source must not be null");
		Assert.notNull(clazz, "clazz must not be null");
		sourceList.forEach(e -> {
			try {
				Object o = clazz.newInstance();
				copyProperties(e, o, pattern);
				targetList.add((T) o);
			} catch (InstantiationException | IllegalAccessException e1) {
				e1.printStackTrace();
			}
		});
		return targetList;
	}

	public static <T> List<T> copyList(Iterable<? extends Object> sourceList, Class<? extends T> clazz) {
		List<T> targetList = new ArrayList<>();
		Assert.notNull(sourceList, "Source must not be null");
		Assert.notNull(clazz, "clazz must not be null");
		sourceList.forEach(e -> {
			try {
				Object o = clazz.newInstance();
				org.springframework.beans.BeanUtils.copyProperties(e, o);
				// org.apache.commons.beanutils.BeanUtils.copyProperties(o, e);
				targetList.add((T) o);
			} catch (InstantiationException | IllegalAccessException e1) {
				e1.printStackTrace();
			}
		});
		return targetList;
	}

	/**
	 * 判断一个类是否为基本数据类型。
	 *
	 * @param clazz
	 *            要判断的类。
	 * @return true 表示为基本数据类型。
	 */
	public static boolean isBaseDataType(Class clazz) throws Exception
	{
		return (
				clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(Boolean.class)
						|| clazz.equals(int.class) || clazz.equals(double.class) || clazz.equals(long.class) || clazz.equals(short.class) || clazz.equals(byte.class) || clazz.equals(boolean.class) || clazz.equals(char.class) || clazz.equals(float.class) || clazz.isPrimitive());
	}
}